# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

import datetime
import functools
import logging
import typing

from coin.exchange.bitmex.kr_rest.product_util import (
    gen_native_symbol,
    parse_native_symbol,
    gen_bitmex_futures_product_list,
)
from coin.exchange.base.kr_rest.futures_product import (
    NativeFuturesProduct,
    NormFuturesProduct,
    parse_futures_product_symbol,
    gen_short_symbol,
)
import coin.exchange.base.kr_rest.derivative_util as derivative_util
import coin.exchange.bitmex.kr_rest.contract_util as contract_util
from coin.exchange.bitmex.kr_rest.currency import (
    BitmexCurrency,)
from coin.exchange.bitmex.kr_rest.constants import coin_list, fiat_list


def get_bitmex_expiry_str_contract_type(expiry_str_raw, current_datetime):
  if current_datetime is None:
    current_datetime = datetime.datetime.now()
  if isinstance(expiry_str_raw, datetime.date):
    expiry_str_raw = expiry_str_raw.strftime("%Y%m%d")
  expiry_str_lower, contract_type = contract_util.to_expiry_str_contract_type(
      expiry_str_raw.lower(), current_datetime)
  return expiry_str_lower.upper(), contract_type


class BitmexFuturesProduct(NativeFuturesProduct):
  _exchange = 'Bitmex'
  NativeCurrencyType = BitmexCurrency

  def __init__(self,
               base: BitmexCurrency,
               quote: BitmexCurrency,
               expiry: typing.Union[datetime.datetime, datetime.date, str],
               native_symbol: str,
               contract_type: str):
    self._contract_type = contract_type

    super().__init__(base=base, quote=quote, expiry=expiry, native_symbol=native_symbol)

    self._subscription_symbol = '%s-%s.%s' % (base.currency, quote.currency, contract_type.upper())

  @property
  def contract_type(self):
    return self._contract_type

  @classmethod
  @functools.lru_cache(maxsize=256)
  def FromProductImpl(cls, product, current_datetime):
    assert isinstance(product, NormFuturesProduct), type(product)
    assert product.quote.currency in ("BTC", "KRW", "JPY", "USD"), str(product.quote)
    expiry_str = derivative_util.to_expiry_str(product.expiry)
    expiry_str, contract_type = get_bitmex_expiry_str_contract_type(expiry_str, current_datetime)
    native_symbol = gen_native_symbol(product.base, product.quote, expiry_str)
    return cls(BitmexCurrency.FromCurrency(product.base),
               BitmexCurrency.FromCurrency(product.quote),
               expiry_str,
               native_symbol,
               contract_type)

  @classmethod
  @functools.lru_cache(maxsize=256)
  def FromStrProductImpl(cls, product, current_datetime):
    parsed_result = parse_futures_product_symbol(product)
    base = BitmexCurrency.FromStrCurrency(parsed_result['base'])
    quote = BitmexCurrency.FromStrCurrency(parsed_result['quote'])
    assert base.native_symbol in coin_list, 'Invalid base: %s' % base
    assert quote.native_symbol in fiat_list, 'Invalid quote: %s' % quote

    expiry_str, contract_type = get_bitmex_expiry_str_contract_type(parsed_result['expiry'],
                                                                    current_datetime)
    native_symbol = gen_native_symbol(base, quote, expiry_str)
    return cls(base, quote, expiry_str, native_symbol, contract_type)

  @classmethod
  @functools.lru_cache(maxsize=256)
  def FromStrNativeProductImpl(cls, product: str, current_datetime):
    parsed_result = parse_native_symbol(product)
    base = BitmexCurrency.FromStrCurrency(parsed_result['base'])
    quote = BitmexCurrency.FromStrCurrency(parsed_result['quote'])

    expiry_str, contract_type = get_bitmex_expiry_str_contract_type(parsed_result['expiry'],
                                                                    current_datetime)
    native_symbol = gen_native_symbol(base, quote, expiry_str)
    return cls(base, quote, expiry_str, native_symbol, contract_type)


def get_bitmex_futures_product_list():
  product_list = []
  result = gen_bitmex_futures_product_list()
  for res in result.values():
    try:
      short_symbol = gen_short_symbol(res['base'], res['quote'], res['expiry'])
      product = BitmexFuturesProduct.FromStrProduct(short_symbol)
      product_list.append(product)
    except Exception as e:
      logging.debug('Fail to generate Bitmex futures product: %s', str(e))
      continue
  return product_list


def get_bitmex_futures_product_list_grouped():
  return [get_bitmex_futures_product_list()]
